import fs from 'fs';

import qiniu from 'qiniu';

import OssException from '../exceptions/oss.exception';
import { QINIU_CONSTANT } from '../config/constant';

import {
  QiniuOssServiceOptionsInterface,
  QiniuOssUploadResponseInterface
} from '../interfaces/services/qiniu-oss-service.interface';

const DEFAULT_QINIU_OPTIONS = {
  scope: QINIU_CONSTANT.SCOPE,
  expires: 7200,  // qiniu default config time(seconds)
  callbackUrl: QINIU_CONSTANT.CALLBACK_URL,
  callbackBody: QINIU_CONSTANT.CALLBACK_BODY,
  callbackBodyType: QINIU_CONSTANT.CALLBACK_BODY_TYPE
};

export class QiniuOssService {
  private mac;

  private bucket;

  private putPolicy;

  private options;

  private zoneConfig;

  constructor(options: QiniuOssServiceOptionsInterface = {}) {
    this.options = {
      ...DEFAULT_QINIU_OPTIONS,
      ...options
    };

    this.bucket = this.options.scope;

    // 创建鉴权对象 mac
    this.mac = new qiniu.auth.digest.Mac(process.env.QINIU_ACCESS_TOKEN, process.env.QINIU_SECRET_TOKEN);

    // 创建 putPolicy 对象
    this.putPolicy = new qiniu.rs.PutPolicy(this.options);

    // 空间参数配置
    this.zoneConfig = new qiniu.conf.Config();
    this.zoneConfig.zone = qiniu.zone.Zone_z0;
  }

  public getUploadToken(): string {
    return this.putPolicy.uploadToken(this.mac);
  }

  // 直接把文件载入内存上传
  public uploadLocalFile(fileName, filePath): Promise<QiniuOssUploadResponseInterface> {
    const formUploader = new qiniu.form_up.FormUploader(this.zoneConfig);

    const putExtra = new qiniu.form_up.PutExtra();

    const uploadToken = this.getUploadToken();

    return new Promise<QiniuOssUploadResponseInterface>((resolve, reject) => {
      formUploader.putFile(uploadToken, fileName, filePath, putExtra, (respErr, respBody, respInfo) => {
        if (respErr) {
          return reject(new OssException(respErr));
        }

        if (respInfo.statusCode == 200) {
          return resolve({
            statusCode: 200,
            body: respBody
          });
        }

        return resolve({
          statusCode: respInfo.statusCode,
          info: respInfo
        });
      });
    });
  }

  // 通过可读流上传
  public uploadByReadableStream(fileName, filePath) {
    const formUploader = new qiniu.form_up.FormUploader(this.zoneConfig);
    const putExtra = new qiniu.form_up.PutExtra();
    const uploadToken = this.getUploadToken();
    const readableStream = fs.createReadStream(filePath);

    return new Promise((resolve, reject) => {
      formUploader.putStream(uploadToken, fileName, readableStream, putExtra, (respErr, respBody, respInfo) => {
        if (respErr) {
          return reject(new OssException(respErr));
        }

        if (respInfo.statusCode == 200) {
          return resolve({
            statusCode: 200,
            body: respBody
          });
        }

        return resolve({
          statusCode: respInfo.statusCode,
          info: respInfo
        });
      });
    });
  }
}
